<?php
use Workerman\Worker;

include __DIR__ . '/vendor/autoload.php';

/*// 证书最好是申请的证书
$context = array(
    'ssl' => array(
        // 请使用绝对路径
        'local_cert'                 => '/etc/nginx/ssl/214908903620893.pem', // 也可以是crt文件
        'local_pk'                   => '/etc/nginx/ssl/214908903620893.key',
        'verify_peer'                => false,
        // 'allow_self_signed' => true, //如果是自签名证书需要开启此选项
    )
);
// 这里设置的是websocket协议（端口任意，但是需要保证没被其它程序占用）
$worker = new Worker('websocket://0.0.0.0:443', $context);*/

// 初始化一个worker容器，监听1234端口
$worker = new Worker('websocket://0.0.0.0:2122');

// ====这里进程数必须必须必须设置为1====
$worker->count = 1;
// 新增加一个属性，用来保存uid到connection的映射(uid是用户id或者客户端唯一标识)
$worker->uidConnections = array();
// 心跳间隔55秒
define('HEARTBEAT_TIME', 55);

// 客户端连上来时，即完成TCP三次握手后的回调
//$worker->onConnect = function ($connection) {
//
//    $connection->send('连接成功');
//
//    /**
//     * 客户端websocket握手时的回调onWebSocketConnect
//     * 在onWebSocketConnect回调中获得nginx通过http头中的X_REAL_IP值
//     */
//    $connection->onWebSocketConnect = function ($connection) {
//        /**
//         * connection对象本没有realIP属性，这里给connection对象动态添加个realIP属性
//         * 记住php对象是可以动态添加属性的，你也可以用自己喜欢的属性名
//         */
//        $connection->realIP = $_SERVER['HTTP_X_REAL_IP'];
//        $connection->send('你的ip是:' . $connection->realIP);
//    };
//
//};

// 当有客户端发来消息时执行的回调函数
$worker->onMessage = function ($connection, $data) {

    // 给connection临时设置一个lastMessageTime属性，用来记录上次收到消息的时间
    $connection->lastMessageTime = time();

    global $worker;

    // 判断当前客户端是否已经验证,即是否设置了uid
    if (!isset($connection->uid)) {
        // 没验证的话把第一个包当做uid（这里为了方便演示，没做真正的验证）
        $connection->uid = $data;
        /* 保存uid到connection的映射，这样可以方便的通过uid查找connection，
         * 实现针对特定uid推送数据
         */
        $worker->uidConnections[$connection->uid] = $connection;
        return $connection->send('登录成功, your uid is ' . $connection->uid);
    }

    switch ($data) {
        case 'ping' :
            $connection->send('ok');
            break;
        default:
            // 其它逻辑，针对某个uid发送 或者 全局广播
            // 假设消息格式为 uid:message 时是对 uid 发送 message
            // uid 为 all 时是全局广播
            list($recv_uid, $message) = explode(':', $data);
            // 全局广播
            if ($recv_uid == 'all') {
                broadcast($message);
            } // 给特定uid发送
            else {
                sendMessageByUid($recv_uid, $message);
            }
            break;
    }

};

// worker进程启动后建立一个内部通讯端口
$worker->onWorkerStart = function ($worker) {
    // 开启一个内部端口，方便内部系统推送数据，Text协议格式 文本+换行符
    $inner_text_worker = new Worker('Text://0.0.0.0:2123');
    $inner_text_worker->onMessage = function ($connection, $buffer) {
        global $worker;

        /*$post_data = array(
            'type' => 'publish',
            'content' => json_encode($json_data),
            'to' => $to_uid,
        );*/

        // $data数组格式，里面有uid，表示向那个uid的页面推送数据
        $data = json_decode($buffer, true);
        $uid = $data['uid'];

        // 通过workerman，向uid的页面推送数据
        $ret = sendMessageByUid($uid, $buffer);

        // 返回推送结果
        $connection->send($ret ? 'ok' : 'fail');
    };
    $inner_text_worker->listen();
};



// 当有客户端连接断开时
$worker->onClose = function ($connection) {
    global $worker;
    if (isset($connection->uid)) {
        // 连接断开时删除映射
        unset($worker->uidConnections[$connection->uid]);
    }
};

// 进程启动后设置一个每秒运行一次的定时器
$worker->onWorkerStart = function($worker) {
    \Workerman\Lib\Timer::add(1, function()use($worker){
        $time_now = time();
        foreach($worker->connections as $connection) {
            // 有可能该connection还没收到过消息，则lastMessageTime设置为当前时间
            if (empty($connection->lastMessageTime)) {
                $connection->lastMessageTime = $time_now;
                continue;
            }
            // 上次通讯时间间隔大于心跳间隔，则认为客户端已经下线，关闭连接
            if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
                $connection->close();
            }
        }
    });
};


// 向所有验证的用户推送数据
function broadcast($message)
{
    global $worker;
    foreach ($worker->uidConnections as $connection) {
        $connection->send($message);
    }
}

// 针对uid推送数据
function sendMessageByUid($uid, $message)
{
    global $worker;
    if (isset($worker->uidConnections[$uid])) {
        $connection = $worker->uidConnections[$uid];
        $connection->send($message);
    }
}

// 运行所有的worker（其实当前只定义了一个）
if (!defined('GLOBAL_START')) {
    Worker::runAll();
}